<!-- 上传 -->
<template>
	<view class="vk-data-upload" :class="classCom" :style="styleCom">
		<!-- 页面开始 -->
		<view v-if="quickPreview">
			<image
				v-if="lists && lists.length" :src="lists[previewIndex].url"
				:style="'width: '+widthCom+';height: ' + heightCom + ';display: block;'"
				:mode="imageMode"
				@click="imagePreviewClick(lists[previewIndex].url)"
			></image>
			<view v-else class="chooseImage-view" :style="{ width: widthCom, height: heightCom }">
				<view>
					<image :src="defaultImage" class="default-image" @click="chooseImage"></image>
				</view>
				<view @click="chooseImage">{{ placeholder }}</view>
				<view class="upload-btn" @click="chooseImage">{{ uploadText }}</view>
			</view>
		</view>

		<view class="header-view" v-if="showHeader">
			<view v-if="title" class="left-view">{{ title }}</view>
			<view v-if="limit > 1" class="right-view">{{ limitTitleCom }}</view>
		</view>

		<view style="padding: 10rpx;" v-if="(quickPreview && lists && lists.length && limit > 1) || !quickPreview">
			<view class="lists-view">
				<view v-for="(item, index) in lists" :key="index" class="image-item-view">
					<image
						:src="item.url"
						class="image-item"
						mode="aspectFill"
						@click="imageItemClick(item.url, index)"
						@touchstart="handleTouchStart($event, item.url, index)"
						@touchmove="handleTouchMove"
						@touchend="handleTouchEnd"
						@longtap="handleLongtap"
					></image>
					<view class="image-mask" v-if="item.error">
						<view class="progress" @click="uploadFile">重试</view>
					</view>
					<view class="image-mask" v-else-if="item.progress < 100">
						<view class="progress">{{ item.progress }}%</view>
					</view>
					<view
						class="image-mask"
						v-else-if="quickPreview && previewIndex == index"
						@touchstart="handleTouchStart($event, item.url, index)"
						@touchmove="handleTouchMove"
						@touchend="handleTouchEnd"
						@longtap="handleLongtap"
					></view>
					<view v-if="!disabled" class="delete-btn" @click="deleteImage(index)">×</view>
				</view>
				<view v-if="lists.length < limit" class="image-item-view upload-btn-sub" @click="chooseImage">
					<view class="item-1"></view>
					<view class="item-2"></view>
				</view>
			</view>
		</view>
		<view v-else-if="quickPreview && lists && lists.length && limit == 1" class="quick-preview-one">
			<button class="quick-preview-upload-btn" @click="reuploadChooseImage">{{ reuploadText }}</button>
		</view>
		<!-- 页面结束 -->
	</view>
</template>

<script>
	/**
	 * vk-data-upload 图片上传组件
	 * @description 图片上传组件
	 * @author VK
	 * @property {String Array} v-model 用于双向绑定输入框的值
	 * @property {String} placeholder placeholder显示值
	 * @property {String} defaultImage 默认图片
	 * @property {String Number} width 快速预览模式下的图片宽度
	 * @property {String Number} height 快速预览模式下的图片高度
	 * @property {String Number} imageWidth 图片item宽度 默认200
	 * @property {Boolean} quickPreview 是否快速预览 默认false
	 * @property {String} imageMode 图片模式 默认aspectFill
	 * @property {String} align 对其方式 默认left
	 * 	@value left						左对齐
	 * 	@value right 					右对齐
	 * @property {Boolean} disabled 是否禁用 默认false
	 * @property {Boolean} previewFullImage 是否在点击预览图后展示全屏图片预览 默认true
	 * @property {Boolean} showHeader 是否显示头部 默认false
	 * @property {Boolean} needSave 是否需要保存到admin素材库 默认false
	 * @property {String} title 头部标题
	 * @property {Number} limit 限制上传数量 默认99
	 * @property {String} provider 云存储提供商
	 * 	@value unicloud				空间内置存储
	 * 	@value extStorage 		扩展存储
	 * 	@value aliyun 				阿里云oss
	 * @property {String} uploadText 上传按钮文字
	 * @property {Array} sizeType 选择图片的尺寸 original 原图，compressed 压缩图，默认二者都有
	 * @property {Array} sourceType 选择图片的来源 album 从相册选图，camera 使用相机，默认二者都有
	 * @property {Number} maxSize 上传图片大小限制
	 * @property {String} cloudPath 云存储路径
	 * @property {Object} uniCloud uniCloud实例
	 * @property {String} cloudDirectory 云存储目录
	 * @property {String} categoryId 云存储分类id
	 * @property {String} env 云存储环境id
	 * @property {Boolean} cloudPathRemoveChinese 云存储路径是否移除中文，默认true
	 * @example <vk-data-upload v-model="images" :limit="9" :show-header="true" title="请上传图片(最多9张)" />
	 */
	export default {
		name: "vk-data-upload",
		emits: ["update:modelValue", "input", "on-progress", "on-success", "on-error", "on-preview", "on-oversize", "on-exceed"],
		props: {
			// 表单值
			value: {},
			modelValue: {},
			// 提示
			placeholder: {
				type: String
			},
			defaultImage: {
				type: String,
				default: ""
			},
			width: {
				type: [String, Number],
				default: 200
			},
			height: {
				type: [String, Number]
			},
			// 是否显示快速预览
			quickPreview: {
				type: Boolean,
				default: false
			},
			// 图片显示模式
			imageMode: {
				type: String,
				default: "aspectFill"
			},
			// 是否禁用
			disabled: {
				type: Boolean,
				default: false
			},
			// 是否在点击预览图后展示全屏图片预览
			previewFullImage: {
				type: Boolean,
				default: true
			},

			showHeader: {
				type: Boolean,
				default: false
			},
			needSave: {
				type: Boolean,
				default: false
			},
			title: {
				type: String,
				default: "上传图片"
			},
			// 最大上传数量
			limit: {
				type: Number,
				default: 99
			},
			// 服务供应商
			provider: {
				type: String
			},
			// 选择图片按钮的提示文字
			uploadText: {
				type: String,
				default: "选择图片"
			},
			// 重新选择图片的提升文字
			reuploadText: {
				type: String,
				default: "重新选择图片"
			},
			// 所选的图片的尺寸, 可选值为original compressed
			sizeType: {
				type: Array,
				default () {
					return ["original", "compressed"];
				}
			},
			// 所选的图片的来源
			sourceType: {
				type: Array,
				default () {
					return ["album", "camera"];
				}
			},
			// left 靠左 right 靠右
			align: {
				type: String,
				default: "left"
			},

			maxSize: {
				type: [String, Number],
				default: 999999999
			},

			cloudPath: {
				type: String
			},
			uniCloud: {
				type: Object
			},
			cloudDirectory: {
				type: String
			},
			categoryId: {
				type: String
			},
			env: {
				type: String
			},
			cloudPathRemoveChinese: {
				type: Boolean,
				default: true
			},
			imageWidth: {
				type: [String, Number],
				default: 200
			}
		},
		data: function() {
			// 组件创建时，进行数据初始化
			return {
				// 预览图
				previewIndex: 0,
				lists: [],
				longPressTimeout: null,
				touchStart: {},
				touchDistance: 0
			};
		},
		mounted() {
			this.init();
		},
		methods: {
			// 初始化
			init() {
				let vk = uni.vk;
				let { valueCom: value, limit } = this;
				if (vk.pubfn.isNull(value) && limit > 1) {
					this._updateValue([]);
				}
			},
			// 向父组件发送更新value事件
			_updateValue(value) {
				let { limit } = this;
				if (limit == 1) {
					if (typeof value === "object") {
						value = value[0];
					}
				}
				this.$emit("input", value);
				this.$emit("update:modelValue", value);
			},
			// 选择图片
			chooseImage() {
				let { sizeType, sourceType, lists, limit, maxSize, disabled } = this;
				if (lists.length >= limit) {
					return false;
				}
				if (disabled) {
					return false;
				}
				uni.chooseImage({
					count: limit, //默认9
					sizeType, //可以指定是原图还是压缩图，默认二者都有
					sourceType, //从相册选择
					success: (res) => {
						res.tempFiles.map((val, index) => {
							if (val.size > maxSize) {
								this.$emit("on-oversize", val, this.lists, this.index);
								uni.showToast({
									title: "超出允许的文件大小",
									icon: "none"
								});
							}
							if (lists.length >= limit) {
								this.$emit("on-exceed", val, this.lists, this.index);
								uni.showToast({
									title: "超出最大允许的文件个数",
									icon: "none"
								});
								return;
							}
							lists.push({
								url: val.path,
								progress: 0,
								error: false,
								file: val
							});
						});
						this.lists = JSON.parse(JSON.stringify(lists));
						this.uploadFile();
					}
				});
			},
			reuploadChooseImage() {
				this.deleteImageFn(0);
				this.chooseImage();
			},
			// 删除图片
			deleteImage(index) {
				let vk = uni.vk;
				let { previewIndex = 0, limit } = this;
				vk.confirm("确定删除吗？", "删除提示", "确定", "取消", (res) => {
					if (res.confirm) {
						this.deleteImageFn(index);
					}
				});
			},
			// 删除图片
			deleteImageFn(index) {
				let vk = uni.vk;
				let { previewIndex = 0, valueCom: value, limit } = this;
				if (vk.pubfn.isNull(value)) {
					this.lists.splice(index, 1);
				}
				if (previewIndex >= this.lists.length - 1) {
					this.previewIndex = 0;
				}
				if (limit > 1) {
					value.splice(index, 1);
					this.lists.splice(index, 1);
				} else {
					value = "";
					this.lists = [];
				}
				this._updateValue(value);
			},
			// 监听内部文件变化 - 实现自定义上传
			async uploadFile() {
				let { lists: list } = this;

				let tasks = [];
				list.map((item, index) => {
					if (item.progress < 100) {
						tasks.push(this.beforeUpload(index, list));
					}
				});
				await Promise.all(tasks);
				let images = [];
				this.lists.map((item, index) => {
					images.push(item.url);
				});
				this._updateValue(images);
			},
			// 上传文件
			beforeUpload(index, list) {
				let vk = uni.vk;
				let {
					limit,
					needSave,
					provider,
					cloudPath,
					uniCloud: myUniCloud,
					cloudDirectory,
					categoryId,
					env,
					cloudPathRemoveChinese
				} = this;
				let tempFilePath = list[index].url;
				let file = list[index].file;
				return vk.uploadFile({
					file,
					filePath: tempFilePath,
					fileType: "image",
					provider,
					needSave,
					cloudPath,
					uniCloud: myUniCloud,
					cloudDirectory,
					category_id: categoryId,
					env,
					cloudPathRemoveChinese,
					onUploadProgress: (res) => {
						if (res.progress > 0) {
							if (list[index]) {
								list[index].progress = res.progress;
								this.$emit("on-progress", res, index, list);
							}
						}
					},
					success: (res) => {
						// 上传成功
						let url = res.url;
						list[index].response = "ok";
						list[index].progress = 100;
						list[index].error = false;
						list[index].url = url;
						this.$emit("on-success", res, index, list);
						let images = this.valueCom;
						if (limit > 1) {
							if (!images || images.length == 0) {
								images = [];
							}
							images.push(url);
						} else {
							images = url;
						}
					},
					fail: (res) => {
						// 上传失败
						list[index].error = true;
						this.$emit("on-error", res, index, list);
					}
				});
			},
			// 图片预览
			previewImage(url, index) {
				let { previewIndex, lists, previewFullImage } = this;
				if (index == undefined) index = previewIndex;
				if (!previewFullImage) {
					this.$emit('on-preview', url, lists, index);
					return;
				}
				const images = lists.map(item => item.url || item.path);
				uni.previewImage({
					urls: images,
					current: url,
					success: () => {
						this.$emit('on-preview', url, lists, index);
					},
					fail: () => {
						uni.showToast({
							title: '预览图片失败',
							icon: 'none'
						});
					}
				});
			},
			// 图片预览前点击事件
			imagePreviewClick(url){
				let list = ['预览图片', this.reuploadText];
				vk.showActionSheet({
					title: '请选择',
					list,
					color: '#000000',
					success: res => {
						let name = list[res.index];
						if (name === "预览图片") {
							this.previewImage(url);
						} else if (name === this.reuploadText) {
							this.reuploadChooseImage();
						}
					}
				});
			},
			// 图片item点击事件
			imageItemClick(url, index) {
				let { quickPreview, previewFullImage, limit } = this;
				this.previewIndex = index;
				if (!quickPreview && previewFullImage && limit === 1) {
					this.previewImage(url, index);
				}
			},
			handleLongtap(event) {
				event.preventDefault();
			},
			// 图片item长按事件
			imageItemLongtap(url, index) {
				let vk = uni.vk;
				let { quickPreview, previewFullImage, valueCom: value, lists, limit } = this;
				this.previewIndex = index;
				if (limit === 1) {
					return;
				}
				let sheetList = ['预览图片', '删除图片'];
				if (index > 0) {
					sheetList.push('向左移动');
				}
				if (index < value.length - 1) {
					sheetList.push('向右移动');
				}
				vk.showActionSheet({
					title: '',
					list: sheetList,
					color: '#000000',
					success: res => {
						let selectValue = sheetList[res.index];
						if (selectValue == '预览图片') {
							if (!quickPreview && previewFullImage) {
								this.previewImage(url, index);
							}
						} else if (selectValue == '向左移动') {
							this.swapPosition(lists, index, index - 1);
						} else if (selectValue == '向右移动') {
							this.swapPosition(lists, index, index + 1);
						} else if (selectValue == '删除图片') {
							this.deleteImage(index);
						}
					}
				});
			},
			// 交换图片位置顺序
			swapPosition(arr, index1, index2) {
				const temp = arr[index1];
				arr[index1] = arr[index2];
				arr[index2] = temp;
				this.lists = arr;
				let images = [];
				this.lists.map((item, index) => {
					images.push(item.url);
				});
				this._updateValue(images);
			},
			handleTouchStart(event, url, index) {
				this.touchStart = event.touches[0];
				event.preventDefault();
				this.imageItemClick(url, index);
				this.longPressTimeout = setTimeout(() => {
					if (!isNaN(this.touchDistance) && this.touchDistance > 5) {
						this.touchDistance = 0;
						this.touchStart = {};
						return;
					}
					this.imageItemLongtap(url, index)
				}, 168);
			},
			handleTouchMove(event) {
				let touchEnd = event.changedTouches[0];
				let { touchStart } = this;
				let offsetX = touchEnd.clientX - touchStart.clientX;
				let offsetY = touchEnd.clientY - touchStart.clientY;
				let distance = Math.sqrt(Math.pow(offsetX, 2) + Math.pow(offsetY, 2));
				this.touchDistance = distance;
			},
			handleTouchEnd() {
				clearTimeout(this.longPressTimeout);
			}
		},
		// 监听器
		watch: {
			// 监听外部value变化
			valueCom: {
				immediate: true,
				handler: function(newVal, oldVal) {
					let lists = [];
					if (newVal) {
						if (this.limit == 1) {
							lists.push({
								url: newVal,
								progress: 100,
								error: false
							});
						} else {
							newVal.map((item, index) => {
								lists.push({
									url: item,
									progress: 100,
									error: false
								});
							});
						}
					}
					this.lists = JSON.parse(JSON.stringify(lists));
				}
			}
		},
		// 计算属性
		computed: {
			// 处理后的value
			valueCom() {
				// #ifdef VUE2
				return this.value;
				// #endif

				// #ifdef VUE3
				return this.modelValue;
				// #endif
			},
			// 处理后的width
			widthCom() {
				let width = this.width;
				return `${Number(width)}rpx`;
			},
			// 处理后的height
			heightCom() {
				let height = this.height;
				return `${Number(height)}rpx`;
			},
			// 处理后的title
			limitTitleCom() {
				let { limit } = this;
				let str = "";
				if (limit > 1) {
					let { valueCom: value } = this;
					if (!value) value = [];
					let length = value.length;
					str = `${length} /  ${limit}`;
				}
				return str;
			},
			classCom(){
				let str = "";
				if (this.quickPreview) {
					if (str) str += " ";
					str += "quick-preview";
				}
				if (this.align) {
					if (str) str += " ";
					str += `align-${this.align}`;
				}
				return str;
			},
			styleCom(){
				let str = "";
				if (this.imageWidth) {
					str += `--item-width:${this.imageWidth}rpx;`;
				}
				console.log('str: ', str)
				return str;
			}
		}
	};
</script>

<style lang="scss" scoped>
	.vk-data-upload {
		width: 100%;
		text-align: left;
		--item-width: 200rpx;

		&.align-right {
			.lists-view {
				justify-content: flex-end;
			}
		}

		.chooseImage-view {
			color: #989898;
			background-color: #f8f8f8;
			display: flex;
			justify-content: center;
			align-items: center;
			flex-direction: column;
		}

		.upload-btn {
			border: 1px solid #52a0d5;
			color: #52a0d5;
			background-color: #ffffff;
			border-radius: 8rpx;
			padding: 20rpx 60rpx;
			margin-top: 20rpx;
			line-height: normal;
			&:active {
				background-color: #f0f0f0;
			}
		}

		.image-mask {
			position: absolute;
			width: 100%;
			height: 100%;
			background-color: rgba(0, 0, 0, 0.4);
			border-radius: 8rpx;
			top: 0;
			left: 0;
			z-index: 2;
			display: flex;
			justify-content: center;
			align-items: center;
		}

		.image-item-view {
			position: relative;
			display: inline-block;
			margin: 10rpx;
			width: var(--item-width);
			height: var(--item-width);

			.image-item {
				width: 100%;
				height: 100%;
				border-radius: 10rpx;
			}
		}

		.delete-btn {
			position: absolute;
			right: -20rpx;
			top: -20rpx;
			font-size: 30rpx;
			width: 40rpx;
			height: 40rpx;
			border-radius: 50%;
			text-align: center;
			line-height: 36rpx;
			z-index: 3;
			background-color: #565656;
			color: #ffffff;
		}

		.progress {
			font-size: 24rpx;
			width: 70rpx;
			height: 70rpx;
			background-color: #ffffff;
			border-radius: 50%;
			text-align: center;
			line-height: 70rpx;
		}

		.upload-btn-sub {
			width: var(--item-width);
			height: var(--item-width);
			background-color: #eff8ff;
			border: 1px solid #c3e1ed;
			display: flex;
			align-items: center;
			justify-content: center;
			position: relative;

			.item-1 {
				position: absolute;
				width: 50%;
				border-bottom: 2px solid #489cd3;
			}

			.item-2 {
				position: absolute;
				border-left: 2px solid #489cd3;
				height: 50%;
			}
		}

		.lists-view {
			display: flex;
			flex-direction: row;
			flex-wrap: wrap;
		}

		.default-image {
			width: 128rpx;
			height: 128rpx;
		}

		.header-view {
			padding: 10rpx 20rpx;
			display: flex;
		}

		.left-view {
			width: 70%;
		}

		.right-view {
			width: 30%;
			text-align: right;
		}
	}

	.vk-data-upload.quick-preview {
		position: relative;

		.delete-btn {
			background-color: #fafafa;
			color: #000000;
		}

		.quick-preview-one {
			position: absolute;
			right: 6px;
			top: 6px;
		}

		.quick-preview-upload-btn {
			height: 60rpx;
			line-height: 60rpx;
			font-size: 24rpx;
			border-radius: 10px;
			overflow: hidden;
			border: 1px solid #f3f3f3;
			color: #ffffff;
			background-color: rgba(0, 0, 0, 0.4);
			&::after {
				border: 0;
				border-radius: 0 0 0 10px;
			}
		}
	}
</style>
